package sys

import (
	"encoding/json"
	"expvar"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/shirou/gopsutil/v3/cpu"
	"github.com/shirou/gopsutil/v3/disk"
	"github.com/shirou/gopsutil/v3/host"
	"github.com/shirou/gopsutil/v3/mem"
	"github.com/shirou/gopsutil/v3/net"
	"github.com/shirou/gopsutil/v3/process"
	"io/ioutil"
	"net/http"
	"os"
	"runtime"
	"runtime/pprof"
	"strconv"
	"strings"
	"time"
)

// 开始时间
var start = time.Now()

// calculateUptime 计算运行时间
func calculateUptime() interface{} {
	return time.Since(start).String()
}

// currentGoVersion 当前 Golang 版本
func currentGoVersion() interface{} {
	return runtime.Version()
}

// getNumCPUs 获取 CPU 核心数量
func getNumCPUs() interface{} {
	return runtime.NumCPU()
}

// getGoOS 当前系统类型
func getGoOS() interface{} {
	return runtime.GOOS
}

// getNumGoroutins 当前 goroutine 数量
func getNumGoroutins() interface{} {
	return runtime.NumGoroutine()
}

// getNumCgoCall CGo 调用次数
func getNumCgoCall() interface{} {
	return runtime.NumCgoCall()
}

// 1.获取 cpu 内存 磁盘使用率
func GetCpuPercent() float64 {
	percent, _ := cpu.Percent(time.Second, false)

	////cpu 使用率  循环获取
	//for i := 5; i > 0; i-- {
	//	fmt.Println("--------------------------")
	//	info, _ := cpu.Percent(time.Second, false)
	//	fmt.Println(info)
	//}
	return percent[0]
}

func GetMemPercent() float64 {
	memInfo, _ := mem.VirtualMemory()
	return memInfo.UsedPercent
}

func GetDiskPercent() float64 {
	parts, _ := disk.Partitions(true)
	diskInfo, _ := disk.Usage(parts[0].Mountpoint)
	return diskInfo.UsedPercent
}

// 2.获取本机信息
func GetHostInfo() {
	info, _ := host.Info()
	fmt.Println(info)
}

// 3. 获取CPU信息
func GetCPUInfo() {
	cpuInfo, _ := cpu.Info()
	fmt.Println(cpuInfo)
}

// 4. 获取内存信息
func GetMemInfo() {
	info, _ := mem.VirtualMemory()
	fmt.Println(info)
	info2, _ := mem.SwapMemory()
	fmt.Println(info2)

}

// 5.获取磁盘信息
func GetDiskInfo() {
	info, _ := disk.Partitions(true) //所有分区
	fmt.Println(info)
	info2, _ := disk.Usage("E:") //指定某路径的硬盘使用情况
	fmt.Println(info2)
	info3, _ := disk.IOCounters() //所有硬盘的io信息
	fmt.Println(info3)
}

// 6.获取网络信息
func GetNetIOCounters() {
	info, _ := net.IOCounters(false)
	fmt.Println(info)
}

// 7.获取进程信息
func GetPIDs() {
	info, _ := process.Pids() //获取当前所有进程的pid
	fmt.Println(info)
	//output：
	//[0 4 96 464 636 740 748 816 852 880 976 348 564 668 912 1048 1120 1184 1268 1288。。。]
	//	info2,_ := process.GetWin32Proc(1120) //对应pid的进程信息
	//fmt.Println(info2)
	//output：
	//[{svchost.exe 0xc00003e570 0xc00003e580 8
	//2019-05-23 09:15:28.444192 +0800 CST 5600 4 0xc00003e5b0 0 0 0 0
	//Win32_ComputerSystem WIN-SE89TTCP7U3 0xc00003e620
	//Win32_Process 0xc00003e650 0xc00005331e 209 6250000 0xc000053378 0xc0000533b8
	//Win32_OperatingSystem Microsoft Windows 10 专业版。。。}]
	//fmt.Println(info2[0].ParentProcessID) //获取父进程的pid
}

// 进程的CPU使用率
func GetCpuUsageOfTheProcess() {
	p, _ := process.NewProcess(int32(os.Getpid()))
	cpuPercent, err := p.Percent(time.Second)
	if err != nil {
		fmt.Println(err)
	}
	cp := cpuPercent / float64(runtime.NumCPU())
	fmt.Println(cp)

}

// 内存使用率、线程数和goroutine数
func MemoryUsageNumberOfThreadsAndNumberOfGoroutines() {
	p, _ := process.NewProcess(int32(os.Getpid()))
	// 获取进程占用内存的比例
	mp, _ := p.MemoryPercent()
	fmt.Println("获取进程占用内存的比例: ", mp)
	// 创建的线程数
	threadCount := pprof.Lookup("threadcreate").Count()
	fmt.Println("创建的线程数: ", threadCount)
	// Goroutine数
	gNum := runtime.NumGoroutine()
	fmt.Println("Goroutine数: ", gNum)
}

//var CuMemoryPtr *map[string]models.Kline
//var BTCMemoryPtr *map[string]models.Kline

// 容器环境下获取进程指标
func grey() {
	cpuPeriod, err := readUint("/sys/fs/cgroup/cpu/cpu.cfs_period_us")
	if err != nil {
		fmt.Println(err)
	}

	cpuQuota, err := readUint("/sys/fs/cgroup/cpu/cpu.cfs_quota_us")
	if err != nil {
		fmt.Println(err)
	}
	cpuNum := float64(cpuQuota) / float64(cpuPeriod)
	fmt.Println("容器环境下获取进程指标: ", cpuNum)
}

// 容器的能使用的最大内存数
func getMaximumAmountOfMemoryAContainerCanUse() {
	p, _ := process.NewProcess(int32(os.Getpid()))
	memLimit, err := readUint("/sys/fs/cgroup/memory/memory.limit_in_bytes")
	if err != nil {
		fmt.Println(err)
	}
	memInfo, err := p.MemoryInfo()
	mp := memInfo.RSS * 100 / memLimit
	fmt.Println("容器的能使用的最大内存数: ", mp)
}

func readUint(path string) (uint64, error) {
	v, err := ioutil.ReadFile(path)
	if err != nil {
		return 0, err
	}
	return parseUint(strings.TrimSpace(string(v)), 10, 64)
}

func parseUint(s string, base, bitSize int) (uint64, error) {
	v, err := strconv.ParseUint(s, base, bitSize)
	if err != nil {
		intValue, intErr := strconv.ParseInt(s, base, bitSize)
		// 1. Handle negative values greater than MinInt64 (and)
		// 2. Handle negative values lesser than MinInt64
		if intErr == nil && intValue < 0 {
			return 0, nil
		} else if intErr != nil &&
			intErr.(*strconv.NumError).Err == strconv.ErrRange &&
			intValue < 0 {
			return 0, nil
		}
		return 0, err
	}
	return v, nil
}

//// 业务特定的内存数据
//func getCuMemoryMap() interface{} {
//	if CuMemoryPtr == nil {
//		return 0
//	} else {
//		return len(*CuMemoryPtr)
//	}
//}

//// 业务特定的内存数据
//func getBTCMemoryMap() interface{} {
//	if BTCMemoryPtr == nil {
//		return 0
//	} else {
//		return len(*BTCMemoryPtr)
//	}
//}

var lastPause uint32

// getLastGCPauseTime 获取上次 GC 的暂停时间
func getLastGCPauseTime() interface{} {
	var gcPause uint64
	ms := new(runtime.MemStats)

	statString := expvar.Get("memstats").String()
	if statString != "" {
		json.Unmarshal([]byte(statString), ms)

		if lastPause == 0 || lastPause != ms.NumGC {
			gcPause = ms.PauseNs[(ms.NumGC+255)%256]
			lastPause = ms.NumGC
		}
	}
	return gcPause
}

// GetCurrentRunningStats 返回当前运行信息
func GetCurrentRunningStats(c *gin.Context) {
	c.Writer.Header().Set("Content-Type", "application/json; charset=utf-8")

	first := true
	report := func(key string, value interface{}) {
		if !first {
			fmt.Fprintf(c.Writer, ",\n")
		}
		first = false
		if str, ok := value.(string); ok {
			fmt.Fprintf(c.Writer, "%q: %q", key, str)
		} else {
			fmt.Fprintf(c.Writer, "%q: %v", key, value)
		}
	}

	fmt.Fprintf(c.Writer, "{\n")
	expvar.Do(func(kv expvar.KeyValue) {
		report(kv.Key, kv.Value)
	})
	fmt.Fprintf(c.Writer, "\n}\n")

	c.String(http.StatusOK, "")
}

func Init() { //这些都是我自定义的变量，发布到expvar中，每次请求接口，expvar会自动去获取这些变量，并返回给我
	expvar.Publish("运行时间", expvar.Func(calculateUptime))
	expvar.Publish("version", expvar.Func(currentGoVersion))
	expvar.Publish("cores", expvar.Func(getNumCPUs))
	expvar.Publish("os", expvar.Func(getGoOS))
	expvar.Publish("cgo", expvar.Func(getNumCgoCall))
	expvar.Publish("goroutine", expvar.Func(getNumGoroutins))
	expvar.Publish("gcpause", expvar.Func(getLastGCPauseTime))
	//expvar.Publish("CuMemory", expvar.Func(getCuMemoryMap))
	//expvar.Publish("BTCMemory", expvar.Func(getBTCMemoryMap))
}
